﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;

namespace AutomationFoundation.CodeToolKit
{
    /// <summary>
    /// Extension method class that provides helper methods for automatically generating source code by language.
    /// </summary>
    internal static class TypeHelper
    {
        #region Constants

        /// <summary>
        /// Parameter is out base C#
        /// </summary>
        private const string CS_OUTPARMOPTION = "out";

        /// <summary>
        /// Parameter is by ref c#
        /// </summary>
        private const string CS_REFPARMOPTION = "ref";

        /// <summary>
        /// Defintion of the void type in C#
        /// </summary>
        private const string CS_VOIDTYPE = "void";

        /// <summary>
        /// Static access type for C#
        /// </summary>
        private const string CS_ACCESS_STATIC = "static";

        /// <summary>
        /// Public access type for C#
        /// </summary>
        private const string CS_ACCESS_PUBLIC = "public";

        /// <summary>
        /// Private access type for C#
        /// </summary>
        private const string CS_ACCESS_PRIVATE = "private";

        /// <summary>
        /// Assembly level access type for C#
        /// </summary>
        private const string CS_ACCESS_ASSEMBLY = "internal";

        /// <summary>
        /// Inherited level access type for C#
        /// </summary>
        private const string CS_ACCESS_FAMILY = "protected";

        /// <summary>
        /// The implementation can be implemented for C#
        /// </summary>
        private const string CS_MODIFIER_CANOVERRIDE = "virtual";

        /// <summary>
        /// The implementation cannot be overriden for C#
        /// </summary>
        private const string CS_MODIFIER_CANNOTOVERRIDE = "sealed";

        /// <summary>
        /// The implementation is being overriddin for C#
        /// </summary>
        private const string CS_MODIFIER_OVERRIDE = "override";


        #endregion

        #region Field Signatures

        /// <summary>
        /// Generates the field signature for the supplied field information.
        /// </summary>
        /// <param name="fieldData">The field information used to genereate the signature.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>The field signature as a string.</returns>
        public static string GenerateFieldSignatureCSharp(this FieldInfo fieldData, bool surpressSystemNamespace)
        {

            if (fieldData == null)
            {
                return string.Empty;
            }

            return GenerateFieldSignatureCSharp(fieldData, surpressSystemNamespace, fieldData.Name);
        }


        /// <summary>
        /// Generates the field signature for the supplied field information.
        /// </summary>
        /// <param name="fieldData">The field information used to genereate the signature.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="fieldName">Name that will replace the default name of the field.</param>
        /// <returns>The field signature as a string.</returns>
        public static string GenerateFieldSignatureCSharp(this FieldInfo fieldData, bool surpressSystemNamespace, string fieldName)
        {

            if (fieldData == null)
            {
                return string.Empty;
            }

            string fieldAccessLevel = GetFieldAccessCSharp(fieldData);

            string returnType = fieldData.FieldType.GetTypeNameCSharp(surpressSystemNamespace);

            return string.Format("{0} {1} {2}", fieldAccessLevel, returnType, fieldName);
        }

        #endregion

        #region Method Signatures

        /// <summary>
        /// Gets the return type for the method.
        /// </summary>
        /// <param name="methodData">The method the return type is being gathered from</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>Returns the value as a string. If no data was found a empty string will be returned.</returns>
        public static string GetReturnTypeCSharp(this MethodInfo methodData, bool surpressSystemNamespace)
        {
            if (methodData != null)
            {
                return GetTypeNameCSharp(methodData.ReturnType, surpressSystemNamespace);
            }

            return string.Empty;
        }

        /// <summary>
        /// Returns the list of parameters as a formatted string
        /// </summary>
        /// <param name="methodData">The method to get the parameters from.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>Returns the value as a string. If no data was found a empty string will be returned.</returns>
        public static string GetParametersCSharp(this MethodInfo methodData, bool surpressSystemNamespace)
        {
            if (methodData != null)
            {
                return CreateMethodParameters(methodData, surpressSystemNamespace);
            }
            return string.Empty;
        }

        /// <summary>
        /// Gets the formatted name of the method. This will also return the generics defintion if the method has a generics definition.
        /// </summary>
        /// <param name="methodData">The reflected method to generate the method from.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>Returns the formatted name as string, or an empty string if the name could not be formatted.</returns>
        public static string GetMethodName(this MethodInfo methodData, bool surpressSystemNamespace)
        {
            if (methodData != null)
            {
                return FormatMethodName(methodData, surpressSystemNamespace, methodData.Name);
            }

            return string.Empty;

        }

        /// <summary>
        /// Generates the method signature for the supplied method information.
        /// </summary>
        /// <param name="methodData">The reflection information about the method to be generated.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateMethodSignatureCSharp(this MethodInfo methodData, bool surpressSystemNamespace)
        {
            if (methodData == null)
            {
                return string.Empty;
            }

            return GenerateMethodSignatureCSharp(methodData, surpressSystemNamespace, methodData.Name);
        }

        /// <summary>
        /// Generates the method signature for the supplied method information.
        /// </summary>
        /// <param name="methodData">The reflection information about the method to be generated.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="methodName">Replace the name of the method with the one provided.</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateMethodSignatureCSharp(this MethodInfo methodData, bool surpressSystemNamespace, string methodName)
        {
            if (methodData == null)
            {
                return string.Empty;
            }

            string accessLevel;

            accessLevel = GetMethodAccessCSharp(methodData);

            string returnType = methodData.ReturnType.GetTypeNameCSharp(surpressSystemNamespace);


            if (methodData.IsAbstract)
            {

                return string.Format("{0} abstract {1} {2}( {3} );",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }

            if (methodData.IsFinal)
            {
                return string.Format("{0} sealed override {1} {2}( {3} )",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }


            if (methodData.IsHideBySig)
            {
                return string.Format("{0} override {1} {2}( {3} )",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }

            if (methodData.IsVirtual)
            {

                return string.Format("{0} virtual {1} {2}( {3} )",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }



            return string.Format("{0} {1} {2}( {3} )",
               accessLevel,
               returnType,
               FormatMethodName(methodData, surpressSystemNamespace, methodName),
               CreateMethodParameters(methodData, surpressSystemNamespace));
        }

        /// <summary>
        /// Generates the method signature for the supplied method information.
        /// </summary>
        /// <param name="methodData">The reflection information about the method to be generated.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="ignoreAbstractDefinition">Boolean flag that will ignore a method in abstract state and implement a normal defintion</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateMethodSignatureCSharp(this MethodInfo methodData, bool surpressSystemNamespace, bool ignoreAbstractDefinition)
        {
            if (methodData == null)
            {
                return string.Empty;
            }

            return GenerateMethodSignatureCSharp(methodData, surpressSystemNamespace, ignoreAbstractDefinition,false, methodData.Name);

        }

        /// <summary>
        /// Generates the method signature for the supplied method information.
        /// </summary>
        /// <param name="methodData">The reflection information about the method to be generated.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="ignoreAbstractDefinition">Boolean flag that will ignore a method in abstract state and implement a normal defintion</param>
        /// <param name="methodName">Replace the name of the method with the one provided.</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateMethodSignatureCSharp(this MethodInfo methodData, bool surpressSystemNamespace, bool ignoreAbstractDefinition, bool isMethodFromInterface, string methodName)
        {
            if (methodData == null)
            {
                return string.Empty;
            }

            string accessLevel;

            accessLevel = GetMethodAccessCSharp(methodData);

            string returnType = methodData.ReturnType.GetTypeNameCSharp(surpressSystemNamespace);


            if (methodData.IsAbstract && !ignoreAbstractDefinition)
            {

                return string.Format("{0} abstract {1} {2}( {3} );",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }

            if (methodData.IsFinal)
            {
                return string.Format("{0} sealed override {1} {2}( {3} )",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }


            if (methodData.IsHideBySig && !isMethodFromInterface)
            {
                return string.Format("{0} override {1} {2}( {3} )",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }

            if (methodData.IsVirtual)
            {

                return string.Format("{0} virtual {1} {2}( {3} )",
                accessLevel,
                returnType,
                FormatMethodName(methodData, surpressSystemNamespace, methodName),
                CreateMethodParameters(methodData, surpressSystemNamespace));
            }



            return string.Format("{0} {1} {2}( {3} )",
               accessLevel,
               returnType,
               FormatMethodName(methodData, surpressSystemNamespace, methodName),
               CreateMethodParameters(methodData, surpressSystemNamespace));
        }
        
        #endregion

        #region Event Signatures

        /// <summary>
        /// Generates the method signature for method that handles the raised event type.
        /// </summary>
        /// <param name="eventData">The information about the event.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="eventHandlerName">The name of the event handler method.</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateEventHandlerMethodSignatureCSharp(this EventInfo eventData, bool surpressSystemNamespace, string eventHandlerName)
        {
            if (eventData == null)
            {
                return string.Empty;
            }

            MethodInfo methodData = eventData.EventHandlerType.GetMethod("Invoke");


            if (methodData == null)
            {
                return string.Empty;
            }

            string accessLevel;

            accessLevel = GetMethodAccessCSharp(methodData);

            string returnType = methodData.ReturnType.GetTypeNameCSharp(surpressSystemNamespace);


            return string.Format("{0} {1} {2}( {3} )",
            accessLevel,
            returnType,
            FormatMethodName(methodData, surpressSystemNamespace, eventHandlerName),
            CreateMethodParameters(methodData, surpressSystemNamespace));
        }

        /// <summary>
        /// Generats the method signature for the method that will raise the event.
        /// </summary>
        /// <param name="eventData">The information about the event.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateRaiseEventMethodSignatureCSharp(this EventInfo eventData, bool surpressSystemNamespace)
        {
            if (eventData == null)
            {
                return string.Empty;
            }

            string methodName = "RaiseEvent" + eventData.Name;

            return GenerateEventMethodSignatureCSharp(eventData, surpressSystemNamespace, methodName);
        }

        /// <summary>
        /// Generats the method signature for the method that will raise the event.
        /// </summary>
        /// <param name="eventData">The information about the event.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="methodName">The name of the raise event method.</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateEventMethodSignatureCSharp(this EventInfo eventData, bool surpressSystemNamespace, string methodName)
        {
            string accessLevel;

            MethodInfo methodData = eventData.EventHandlerType.GetMethod("Invoke");

            accessLevel = GetMethodAccessCSharp(methodData);

            return GenerateEventMethodSignatureCSharp(eventData, surpressSystemNamespace, methodName, accessLevel);
        }

        /// <summary>
        /// Generats the method signature for the method that will raise the event.
        /// </summary>
        /// <param name="eventData">The information about the event.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="methodName">The name of the raise event method.</param>
        /// <param name="accessLevel">The access level you wish to have this eventMethodSignature set to.</param>
        /// <returns>The method signature as a string.</returns>
        public static string GenerateEventMethodSignatureCSharp(this EventInfo eventData, bool surpressSystemNamespace, string methodName, string accessLevel)
        {
            if (eventData == null)
            {
                return string.Empty;
            }

            MethodInfo methodData = eventData.EventHandlerType.GetMethod("Invoke");


            if (methodData == null)
            {
                return string.Empty;
            }

            string returnType = methodData.ReturnType.GetTypeNameCSharp(surpressSystemNamespace);

            return string.Format("{0} {1} {2}( {3} )",
            accessLevel,
            returnType,
            FormatMethodName(methodData, surpressSystemNamespace, methodName),
            CreateMethodParameters(methodData, surpressSystemNamespace));
        }

        /// <summary>
        /// Generates an Event Signature based on the event information.
        /// </summary>
        /// <param name="eventData">The source event information.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>The event signature as a string.</returns>
        public static string GenerateEventSignatureCSharp(this EventInfo eventData, bool surpressSystemNamespace)
        {
            if (eventData == null)
            {
                return string.Empty;
            }

            return GenerateEventSignatureCSharp(eventData, surpressSystemNamespace, eventData.Name);
        }

        /// <summary>
        /// Generates an Event Signature based on the event information.
        /// </summary>
        /// <param name="eventData">The source event information.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="eventName">Name that will be used to replace the name of the event.</param>
        /// <returns>The event signature as a string.</returns>
        public static string GenerateEventSignatureCSharp(this EventInfo eventData, bool surpressSystemNamespace, string eventName)
        {
            if (eventData == null)
            {
                return string.Empty;
            }

            string eventAccess = GetEventAccessCSharp(eventData);

            string eventType = eventData.EventHandlerType.GetTypeNameCSharp(surpressSystemNamespace);

            return string.Format("{0} {1} {2}", eventAccess, eventType, eventName);
        }

        /// <summary>
        /// Returns the parameters of the delegate that supports this event.
        /// </summary>
        /// <param name="eventData">The event the delegates will be extracted from.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>Returns the fully formated list of parameters. In the case that the Invoke method of the event does not exist then a empty string will be returned.</returns>
        public static string GetDelegateParameters(this EventInfo eventData, bool surpressSystemNamespace)
        {

            MethodInfo methodData = eventData.EventHandlerType.GetMethod("Invoke");
            if (methodData != null)
            {
                return CreateMethodParameters(methodData, surpressSystemNamespace);
            }
            return string.Empty;
        }

       
        #endregion

        #region Property Signatures

        /// <summary>
        /// Generates the signature for the property. This does not apply the get or set statements for the property;
        /// </summary>
        /// <param name="propertyData">The parameter being built.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>The property signature as a string.</returns>
        public static string GeneratePropertySignatureCSharp(this PropertyInfo propertyData, bool surpressSystemNamespace)
        {
            if (propertyData == null)
            {
                return string.Empty;
            }
            return GeneratePropertySignatureCSharp(propertyData, surpressSystemNamespace, propertyData.Name);
        }

        /// <summary>
        /// Generates the signature for the property. This does not apply the get or set statements for the property;
        /// </summary>
        /// <param name="propertyData">The parameter being built.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="propertyName">Will replace the property name with the name provided.</param>
        /// <returns>The property signature as a string.</returns>
        public static string GeneratePropertySignatureCSharp(this PropertyInfo propertyData, bool surpressSystemNamespace, string propertyName)
        {
            if (propertyData == null)
            {
                return string.Empty;
            }

            string propertyAccessLevel = GetPropertyAccessCSharp(propertyData);

            string propertyType = propertyData.PropertyType.GetTypeNameCSharp(surpressSystemNamespace);

            return string.Format("{0} {1} {2}", propertyAccessLevel, propertyType, propertyName);
        }

        /// <summary>
        /// Returns the access level for the set method for the property.
        /// </summary>
        /// <param name="propertData">The property to determine the access level.</param>
        /// <returns>The access level for set or null if it is the same as the parent, or if no set is avaiable for the property.</returns>
        public static string GetSetAccessLevelCSharp(this PropertyInfo propertyData)
        {
            if (!propertyData.CanWrite)
            {
                return null;
            }

            string propertyAccessLevel = GetPropertyAccessCSharp(propertyData);

            string setAccessLevel = GetMethodAccessCSharp(propertyData.GetSetMethod(true));

            if (string.Compare(propertyAccessLevel, setAccessLevel) == 0)
            {
                return null;
            }

            return setAccessLevel;
        }

        /// <summary>
        /// Returns the access level for the get method of the property.
        /// </summary>
        /// <param name="propertData">The property to determine the access level.</param>
        /// <returns>The access level for set or null if it is the same as the parent, or if no set is avaiable for the property.</returns>
        public static string GetGetAccessLevelCSharp(this PropertyInfo propertyData)
        {
            if (!propertyData.CanRead)
            {
                return null;
            }

            string propertyAccessLevel = GetPropertyAccessCSharp(propertyData);

            string getAccessLevel = GetMethodAccessCSharp(propertyData.GetGetMethod(true));

            if (string.Compare(propertyAccessLevel, getAccessLevel) == 0)
            {
                return null;
            }

            return getAccessLevel;
        }

        #endregion

        #region Type Signatures

        /// <summary>
        /// Formats a data type and returns the string representation of the type in c# syntax
        /// </summary>
        /// <remarks>
        /// The surpressSystemNamespace flag will only work on the 'base' classes living in the System namespace.
        /// Example: System.Double
        /// The surpressSystemNamespace flag will NOT work on other classes living in System derived namespaces.
        /// Example: System.Collections.ObjectModel.ObservableCollection<>
        /// </remarks>
        /// <param name="typeData">System type of the data being formatted to a string.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>The string formatted version of the data type for use in c sharp code.</returns>
        public static string GetTypeNameCSharp(this System.Type typeData, bool surpressSystemNamespace)
        {
            bool reallySurpressSystemNamespace = surpressSystemNamespace;

            if (surpressSystemNamespace)
            {
                List<Type> typesToSurpressNamespace = new List<Type>
                {
                    typeof(Boolean), //bool
                    typeof(Byte), //byte
                    typeof(Char), //char
                    typeof(Decimal), //decimal
                    typeof(Double), //double
                    typeof(Single), //float
                    typeof(Int32), //int
                    typeof(Int64), //long
                    typeof(Object), //object
                    typeof(SByte), //sbyte
                    typeof(Int16), //short
                    typeof(String), //string
                    typeof(UInt32), //uint
                    typeof(UInt64), //ulong
                    typeof(UInt16), //ushort
                    typeof(EventHandler), //EventHandler
                    typeof(EventHandler<>), //EventHandler<>
                    typeof(EventArgs) //EventArgs
                };

                if (typesToSurpressNamespace.Any(x => x.Name.Equals(typeData.Name)) == false)
                {
                    //they requested a system namespace surpression, but it's not a 'base' type. 
                    //you need the system namespace prefix for automation to work properly.
                    //example: System.Collections.ObjectModel.ObservableCollection<>
                    //don't surpress the system namespace

                    reallySurpressSystemNamespace = false;
                }
            }

            if (typeData == null)
            {
                return string.Empty;
            }

            //Temporary strings used to hold parts of the type name
            string formattedType = string.Empty;
            string genericParameters = string.Empty;
            string baseType = string.Empty;
            string rawBaseType = typeData.FullName;

            //Making sure a generic placeholder was not passed.
            if (rawBaseType == null)
            {
                rawBaseType = typeData.Name;
            }

            //Getting the generic parts of this type if they exist
            Type[] genericTypes = typeData.GetGenericArguments();

            //Getting the number of generic types exist in this type
            int count = genericTypes.Count();

            //boolean flag used to determine if this is a generic type
            bool isGeneric = false;

            //if there are any generic types set the generic flag to true
            if (count != 0)
            {
                isGeneric = true;
            }

            //Removing the reference character from the type name
            if (typeData.IsByRef)
            {
                rawBaseType = rawBaseType.Replace("&", "");
            }

            //Removing the generic reference from the type name
            if (isGeneric)
            {
                rawBaseType = rawBaseType.Remove(rawBaseType.IndexOf("`"));
            }

            //Setting the base type and removeing system. from the namespace if requested.
            if (reallySurpressSystemNamespace)
            {
                baseType = RemoveSystemNameSpaceCS(rawBaseType);
            }
            else
            {
                baseType = rawBaseType;
            }

            if (isGeneric)
            {
                StringBuilder genTypes = new StringBuilder();

                foreach (var item in genericTypes)
                {

                    count = count - 1;

                    genTypes.Append(item.GetTypeNameCSharp(surpressSystemNamespace));

                    if (count > 0)
                    {
                        genTypes.Append(", ");
                    }
                }

                genericParameters = genTypes.ToString();

                formattedType = string.Format("{0}<{1}>", baseType, genericParameters);
            }
            else
            {
                formattedType = baseType;
            }
            return CheckTypeForCS(formattedType);
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Helper method that formats the name of the method. This is mainly for adding generic defintions to the end of the method name.
        /// </summary>
        /// <param name="methodData">The method information.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <param name="methodName">The name of the method before generics are added</param>
        /// <returns>The fully formatted name of the method. If there is no generics implementation for this method then the method name will be returned.</returns>
        static public string FormatMethodName(MethodInfo methodData, bool surpressSystemNamespace, string methodName)
        {
            string formattedMethodName = methodName;

            if (methodData == null)
            {
                return string.Empty;
            }

            if (methodData.IsGenericMethod | methodData.IsGenericMethodDefinition)
            {
                Type[] genTypes = methodData.GetGenericArguments();

                int genCount = genTypes.Count();

                StringBuilder genTypeName = new StringBuilder();
                foreach (var genItems in genTypes)
                {
                    genCount = genCount - 1;

                    genTypeName.Append(genItems.GetTypeNameCSharp(surpressSystemNamespace));

                    if (genCount > 0)
                    {
                        genTypeName.Append(", ");
                    }

                }

                formattedMethodName = string.Format("{0}<{1}>", methodName, genTypeName.ToString());

            }

            return formattedMethodName;
        }

        /// <summary>
        /// Helper method that generates the formatted list of parameters for a method.
        /// </summary>
        /// <param name="methodData">The method the paramters are being created for.</param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>String formatted with each parameter for the method.</returns>
        static public string CreateMethodParameters(MethodInfo methodData, bool surpressSystemNamespace)
        {
            //Formatting paramters for the method.
            StringBuilder paremeters = new StringBuilder();
            ParameterInfo[] Parms = methodData.GetParameters();
            if (Parms == null)
            {
                return string.Empty;
            }
            else
            {
                int count = Parms.Count();
                foreach (var item in Parms)
                {
                    count = count - 1;

                    paremeters.Append(FormatParameterCS(item, surpressSystemNamespace));

                    //If there are still parameters to process add a comman and continue.
                    if (count > 0)
                    {
                        paremeters.Append(", ");
                    }
                }
            }

            return paremeters.ToString();
        }

        /// <summary>
        /// Helper method that removes the system namespace from the beginning of a string.
        /// </summary>
        /// <param name="info">The information to be updated</param>
        /// <returns>string with the system namespace removed.</returns>
        private static string RemoveSystemNameSpaceCS(string info)
        {
            if (info != null)
            {
                return Regex.Match(info, @"(?<=System\.)(.*)|^(?!System\.)(.*)").Value;
            }

            return string.Empty;
        }

        /// <summary>
        /// Formats a parameter used in a method signature. 
        /// </summary>
        /// <param name="parmData"></param>
        /// <param name="surpressSystemNamespace">Boolean flag that determines if types in the system namespace should have the System. removed from the type signature.</param>
        /// <returns>The string equivilant of a method parameter.</returns>
        public static string FormatParameterCS(System.Reflection.ParameterInfo parmData, bool surpressSystemNamespace)
        {
            if (parmData == null)
            {
                return string.Empty;
            }

            bool hasParmOption = false;
            string parmOption = string.Empty;

            if (parmData.IsOut)
            {
                parmOption = CS_OUTPARMOPTION;
                hasParmOption = true;
            }

            if (!parmData.IsOut && parmData.ParameterType.IsByRef)
            {
                parmOption = CS_REFPARMOPTION;
                hasParmOption = true;
            }

            string parmType = parmData.ParameterType.GetTypeNameCSharp(surpressSystemNamespace);

            string parmReturn = string.Empty;

            if (hasParmOption)
            {
                parmReturn = string.Format("{0} {1} {2}", parmOption, parmType, parmData.Name);
            }
            else
            {
                parmReturn = string.Format("{0} {1}", parmType, parmData.Name);
            }

            return parmReturn;
        }

        /// <summary>
        /// Helper method that determines if the System.Void type has been passed in. If it has then it will convert it to void.
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <returns></returns>
        public static string CheckTypeForCS(string typeInfo)
        {
            if (typeInfo == null)
            {
                return string.Empty;
            }

            if (Regex.IsMatch(typeInfo, @"(?<=System\.)Void|Void"))
            {
                return CS_VOIDTYPE;
            }

            return typeInfo;
        }

        /// <summary>
        /// Generate the access level information for the field
        /// </summary>
        /// <param name="info">Reflection object that owns the access level information.</param>
        /// <returns>The access level as a string with a space at the end of the access level.</returns>
        public static string GetFieldAccessCSharp(System.Reflection.FieldInfo info)
        {
            StringBuilder returnValue = new StringBuilder();

            if (info == null)
            {
                return string.Empty;
            }

            if (info.IsStatic)
            {
                returnValue.Append(CS_ACCESS_STATIC);
                returnValue.Append(" ");
            }
            if (info.IsPublic)
            {
                returnValue.Append(CS_ACCESS_PUBLIC);
                return returnValue.ToString();
            }
            if (info.IsPrivate)
            {
                returnValue.Append(CS_ACCESS_PRIVATE);
                return returnValue.ToString();
            }


            if (info.IsFamily)
            {
                returnValue.Append(CS_ACCESS_FAMILY);
            }

            if (info.IsAssembly)
            {
                returnValue.Append(CS_ACCESS_ASSEMBLY);
            }

            if (info.IsFamilyOrAssembly)
            {
                returnValue.Append(CS_ACCESS_FAMILY);
                returnValue.Append(" ");
                returnValue.Append(CS_ACCESS_ASSEMBLY);
            }
            return returnValue.ToString();
        }

        /// <summary>
        /// Generate the access level information for the Method
        /// </summary>
        /// <param name="info">Reflection object that owns the access level information.</param>
        /// <returns>The access level as a string.</returns>
        public static string GetMethodAccessCSharp(this System.Reflection.MethodInfo info)
        {
            StringBuilder returnValue = new StringBuilder();

            if (info == null)
            {
                return string.Empty;
            }

            if (info.IsStatic)
            {
                returnValue.Append(CS_ACCESS_STATIC);
                returnValue.Append(" ");
            }
            if (info.IsPublic)
            {
                returnValue.Append(CS_ACCESS_PUBLIC);
                return returnValue.ToString();
            }
            if (info.IsPrivate)
            {
                returnValue.Append(CS_ACCESS_PRIVATE);
                return returnValue.ToString();
            }


            if (info.IsFamily)
            {
                returnValue.Append(CS_ACCESS_FAMILY);
            }

            if (info.IsAssembly)
            {
                returnValue.Append(CS_ACCESS_ASSEMBLY);
            }

            if (info.IsFamilyOrAssembly)
            {
                returnValue.Append(CS_ACCESS_FAMILY);
                returnValue.Append(" ");
                returnValue.Append(CS_ACCESS_ASSEMBLY);
            }
            return returnValue.ToString();
        }

        /// <summary>
        /// Generate the access level information for the property
        /// </summary>
        /// <param name="info">Reflection object that owns the access level information.</param>
        /// <returns>The access level as a string.</returns>
        public static string GetPropertyAccessCSharp(this System.Reflection.PropertyInfo info)
        {
            if (info == null)
            {
                return string.Empty;
            }

            MethodInfo getInfo;
            MethodInfo setInfo;


            getInfo = info.GetGetMethod(true);
            setInfo = info.GetSetMethod(true);

            StringBuilder returnValue = new StringBuilder();

            #region Static Check

            bool staticSet = false;

            if (info.CanRead)
            {
                if (getInfo.IsStatic)
                {
                    returnValue.Append(CS_ACCESS_STATIC);
                    returnValue.Append(" ");
                    staticSet = true;
                }
            }

            if (info.CanWrite && !staticSet)
            {
                if (setInfo.IsStatic)
                {
                    returnValue.Append(CS_ACCESS_STATIC);
                    returnValue.Append(" ");
                }

            }

            #endregion

            #region public Check

            bool publicSet = false;

            if (info.CanRead)
            {
                if (getInfo.IsPublic)
                {
                    returnValue.Append(CS_ACCESS_PUBLIC);
                    publicSet = true;
                }

            }
            if (info.CanWrite && !publicSet)
            {
                if (setInfo.IsPublic)
                {
                    returnValue.Append(CS_ACCESS_PUBLIC);
                    publicSet = true;
                }
            }

            if (publicSet)
            {
                return returnValue.ToString();
            }

            #endregion

            #region Private Check

            bool privateSet = false;

            if (info.CanRead)
            {
                if (getInfo.IsPrivate)
                {
                    returnValue.Append(CS_ACCESS_PRIVATE);
                    privateSet = true;
                }
            }

            if (info.CanWrite && !privateSet)
            {
                if (setInfo.IsPrivate)
                {
                    returnValue.Append(CS_ACCESS_PRIVATE);
                    privateSet = true;
                }
            }

            if (privateSet)
            {
                return returnValue.ToString();
            }

            #endregion

            #region Protected and internal check

            bool protectedSet = false;

            if (info.CanRead)
            {
                if (getInfo.IsFamily)
                {
                    returnValue.Append(CS_ACCESS_FAMILY);
                    protectedSet = true;
                }
            }

            if (info.CanWrite && !protectedSet)
            {
                if (setInfo.IsFamily)
                {
                    returnValue.Append(CS_ACCESS_FAMILY);
                    protectedSet = true;
                }
            }

            bool internalSet = false;

            if (info.CanRead)
            {
                if (getInfo.IsAssembly)
                {
                    returnValue.Append(CS_ACCESS_ASSEMBLY);
                    internalSet = true;
                }
            }

            if (info.CanWrite && !internalSet)
            {
                if (setInfo.IsAssembly)
                {
                    returnValue.Append(CS_ACCESS_ASSEMBLY);
                    internalSet = true;
                }
            }

            bool protectedInternalSet = false;

            if (info.CanRead)
            {
                if (getInfo.IsFamilyOrAssembly)
                {
                    returnValue.Append(CS_ACCESS_FAMILY);
                    returnValue.Append(" ");
                    returnValue.Append(CS_ACCESS_ASSEMBLY);
                    protectedInternalSet = true;
                }
            }

            if (info.CanWrite && !protectedInternalSet)
            {
                if (setInfo.IsFamilyOrAssembly)
                {
                    returnValue.Append(CS_ACCESS_FAMILY);
                    returnValue.Append(" ");
                    returnValue.Append(CS_ACCESS_ASSEMBLY);
                    protectedInternalSet = true;
                }
            }
            #endregion

            return returnValue.ToString();
        }

        /// <summary>
        /// Generate the access level information for the property
        /// </summary>
        /// <param name="info">Reflection object that owns the access level information.</param>
        /// <returns>The access level as a string</returns>
        public static string GetEventAccessCSharp(this System.Reflection.EventInfo info)
        {

            if (info == null)
            {
                return string.Empty;
            }

            MethodInfo addInfo;
            MethodInfo removeInfo;

            addInfo = info.GetAddMethod(true);
            removeInfo = info.GetRemoveMethod(true);

            StringBuilder returnValue = new StringBuilder();

            #region Static Check

            bool staticSet = false;


            if (addInfo.IsStatic)
            {
                returnValue.Append(CS_ACCESS_STATIC);
                returnValue.Append(" ");
                staticSet = true;
            }


            if (!staticSet)
            {
                if (removeInfo.IsStatic)
                {
                    returnValue.Append(CS_ACCESS_STATIC);
                    returnValue.Append(" ");
                }

            }

            #endregion

            #region public Check

            bool publicSet = false;


            if (addInfo.IsPublic)
            {
                returnValue.Append(CS_ACCESS_PUBLIC);
                publicSet = true;
            }


            if (!publicSet)
            {
                if (removeInfo.IsPublic)
                {
                    returnValue.Append(CS_ACCESS_PUBLIC);
                    publicSet = true;
                }
            }

            if (publicSet)
            {
                return returnValue.ToString();
            }

            #endregion

            #region Private Check

            bool privateSet = false;


            if (addInfo.IsPrivate)
            {
                returnValue.Append(CS_ACCESS_PRIVATE);
                privateSet = true;
            }


            if (!privateSet)
            {
                if (removeInfo.IsPrivate)
                {
                    returnValue.Append(CS_ACCESS_PRIVATE);
                    privateSet = true;
                }
            }

            if (privateSet)
            {
                return returnValue.ToString();
            }

            #endregion

            #region Protected and internal check

            bool protectedSet = false;


            if (addInfo.IsFamily)
            {
                returnValue.Append(CS_ACCESS_FAMILY);
                protectedSet = true;
            }


            if (!protectedSet)
            {
                if (removeInfo.IsFamily)
                {
                    returnValue.Append(CS_ACCESS_FAMILY);
                    protectedSet = true;
                }
            }

            bool internalSet = false;


            if (addInfo.IsAssembly)
            {
                returnValue.Append(CS_ACCESS_ASSEMBLY);
                internalSet = true;
            }


            if (!internalSet)
            {
                if (removeInfo.IsAssembly)
                {
                    returnValue.Append(CS_ACCESS_ASSEMBLY);
                    internalSet = true;
                }
            }

            bool protectedInternalSet = false;

            if (addInfo.IsFamilyOrAssembly)
            {
                returnValue.Append(CS_ACCESS_FAMILY);
                returnValue.Append(" ");
                returnValue.Append(CS_ACCESS_ASSEMBLY);
                protectedInternalSet = true;
            }


            if (!protectedInternalSet)
            {
                if (removeInfo.IsFamilyOrAssembly)
                {
                    returnValue.Append(CS_ACCESS_FAMILY);
                    returnValue.Append(" ");
                    returnValue.Append(CS_ACCESS_ASSEMBLY);
                    internalSet = true;
                }
            }
            #endregion

            return returnValue.ToString();
        }

        #endregion
    }
}
